package basic;

import java.util.Calendar;

import enums.Frequency;

//@author A0098891H
public class Date implements Comparable<Date>{

	public static final String NA_DATE = "NA";
	
	private int _day;
	private int _month;
	private int _year;

	/**
	 * Default constructor
	 * @Precondition none
	 * @Postcondition the day, year, month are all set to 0.
	 */
	public Date()
	{ 
	    _day = 0;
	    _month = 0;
	    _year = 0;
	}

	public Date(Date d)
	{ 
	    _day = d._day;
	    _month = d._month;
	    _year = d._year;
	}
	/**
	 * Constructor with parameters
	 * @param day a calendar day. Integer
	 * @param month a calendar month. Integer
	 * @param year a calendar year. Integer
	 * @Precondition 1<=day<=the max day in that month 1<=month<=12 0<=year<=9999
	 * @Postcondition the day, month, year are set to the given date
	 */
	public Date(int day, int month,int year)
	{
	    _day = day;
	    _month = month;
	    _year = year;
	}
	
	public void set(Date d)
	{
	    _day = d._day;
	    _month = d._month;
	    _year = d._year;
	}
	/**
	 * a static method to get the current date with day, month, year.
	 * @return a Date object of the current date.
	 * @Precondition none
	 * @Postcondition a Date object is returned.
	 */
	public static Date getCurrentDate()
	{
		Calendar cal = Calendar.getInstance();  
	    int year = cal.get(Calendar.YEAR);  
	    int month = cal.get(Calendar.MONTH)+1; //zero-based
	    int day = cal.get(Calendar.DAY_OF_MONTH);
	    
	    return new Date(day, month, year);
	}

	/**
	 * a static method to get the max day in the current month.
	 * @return the max day in the current month. Integer. 
	 * @Precondition none
	 * @Postcondition a max day in current month is returned.
	 */
	public static int getCurMonthMaxDate()
	{
		Calendar cal = Calendar.getInstance();  
		return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
	}
	
	public static int getCurMonthMaxWeek()
	{
		Calendar cal = Calendar.getInstance();  
		return cal.getActualMaximum(Calendar.WEEK_OF_MONTH);
	}

	public static int getCurYearMaxDay()
	{
		Calendar cal = Calendar.getInstance();  
		return cal.getActualMaximum(Calendar.DAY_OF_YEAR);
	}
	public static int getCurYearMaxWeek()
	{
		Calendar cal = Calendar.getInstance();  
		return cal.getActualMaximum(Calendar.WEEK_OF_YEAR);
	}
	
	/**
	 * get the related date calculated by adding/subtracting a number of year/month/week/day.
	 * the parameters < 0 means subtracting, > 0 means adding, = 0 means the current.
	 * @param year a number of year. Integer. 
	 * @param month a number of month. Integer.
	 * @param week a number of week. Integer.
	 * @param day a number of day. Integer.
	 * @return a related Date Object.
	 * @Precondition none
	 * @Postcondition a Date object is returned.
	 */
	public Date getRelatedDate(int year, int month, int week, int day)
	{
		Calendar cal = Calendar.getInstance();  
		cal.set(_year,_month-1,_day);
		cal.add(Calendar.YEAR, year); 
		cal.add(Calendar.MONTH, month); 
		cal.add(Calendar.WEEK_OF_YEAR, week); 
		cal.add(Calendar.DAY_OF_MONTH, day);
		return new Date(cal.get(Calendar.DAY_OF_MONTH),cal.get(Calendar.MONTH)+1,cal.get(Calendar.YEAR));
	}
	
	/**
	 * a static method to get the last start date of a period respect to the current date according to the repeating frequency.
	 * For example, if the param type is Frequency.MONTHLY, and the param date is 20 Jan. 2013. 
	 * The current date is 1 Apr. 2013. This method will return 20 Mar. 2013.
	 * @param date the start date of a period
	 * @param type Frequency.DAILY/Frequency.WEEKLY/Frequency.MONTHLY/Frequency.YEARLY
	 * @return a new Date object which is the start of next period. if Frequency type not found, an NA date is returned.
	 * @Precondition date != null type = Frequency.DAILY/Frequency.WEEKLY/Frequency.MONTHLY/Frequency.YEARLY
	 * @Postcondition a date object is returned.
	 */
	public static Date lastDateAccordingly(Date date, Frequency type)
	{
		Date today = Date.getCurrentDate();
		switch(type)
		{
		case DAILY:
			while(!date.getRelatedDate(0, 0, 0, 1).isGreaterThan(today))
			{
				date = date.getRelatedDate(0, 0, 0, 1);
			}
			break;
		case WEEKLY:
			while(!date.getRelatedDate(0, 0, 1, 0).isGreaterThan(today))
			{
				date = date.getRelatedDate(0, 0, 1, 0);
			}
			break;
		case MONTHLY:
			while(!date.getRelatedDate(0, 1, 0, 0).isGreaterThan(today))
			{
				date = date.getRelatedDate(0, 1, 0, 0);
			}
			break;
		case YEARLY:
			while(!date.getRelatedDate(1, 0, 0, 0).isGreaterThan(today))
			{
				date = date.getRelatedDate(1, 0, 0, 0);
			}
			break;
		default:
			date = new Date(-1,-1,-1);
			break;
		}
		return date;
	}
	
	/**
	 * Compare whether this date is >= another date
	 * @param d another date. Date
	 * @return true if this date >= another date; false of this date < another date
	 * @Precondition date != null
	 * @Postcondition return true/false
	 */
	public boolean isGreaterOrEqual(Date date)
	{
		Calendar cal1 = Calendar.getInstance();
		cal1.set(date.get_year(), date.get_month()-1, date.get_day());
		Calendar cal2 = Calendar.getInstance();
		cal2.set(this.get_year(), this.get_month()-1, this.get_day());
		int value = cal2.compareTo(cal1);
		
		if (value >= 0) {
			return true;
		}
		else
			return false;
	}
	public boolean isGreaterThan(Date date)
	{
		Calendar cal1 = Calendar.getInstance();
		cal1.set(date.get_year(), date.get_month()-1, date.get_day());
		Calendar cal2 = Calendar.getInstance();
		cal2.set(this.get_year(), this.get_month()-1, this.get_day());
		int value = cal2.compareTo(cal1);
		
		if (value > 0) {
			return true;
		}
		else
			return false;
	}

	public boolean isLessThan(Date date)
	{
		Calendar cal1 = Calendar.getInstance();
		cal1.set(date.get_year(), date.get_month()-1, date.get_day());
		Calendar cal2 = Calendar.getInstance();
		cal2.set(this.get_year(), this.get_month()-1, this.get_day());
		int value = cal2.compareTo(cal1);
		
		if (value < 0) {
			return true;
		}
		else
			return false;
	}
	
	/**
	 * 
	 * @return the day of the date
	 */
	public int get_day() {
		return _day;
	}

	/**
	 * 
	 * @param day a calendar day
	 * @Precondition 1<=day<=the max day in the respected month
	 * @Postcondition the day is changed.
	 */
	public void set_day(int day) {
		_day = day;
	}

	/**
	 * 
	 * @return the month of the date
	 */
	public int get_month() {
		return _month;
	}

	/**
	 * 
	 * @param month a calendar month
	 * @Precondition 1<=month<=12
	 * @Postcondition the month is changed
	 */
	public void set_month(int month) {
		_month = month;
	}

	/**
	 * 
	 * @return the year of the date
	 */
	public int get_year() {
		return _year;
	}

	/**
	 * 
	 * @param a calendar year
	 * @Precondition 0<=year<=9999
	 * @Postcondition the year is changed
	 */
	public void set_year(int year) {
		_year = year;
	}

	/**
	 *
	 * @return String of date in the format DD/MM/YYYY
	 */
	public String toString()
	{
		if(_year==0 && _month==0 && _day==0)
		{
			return "";
		}
		else if(_year==-1&&_month==-1&&_day==-1)
		{
			return NA_DATE;
		}
		return _day+"/"+_month+"/"+_year;
	}
	
	/**
	 * Check whether two date are equal according to the day, month, and year
	 * @param date another date
	 * @return true if two days are equal, false otherwise
	 * @Precondition date != null
	 * @Postcondition return true/false
	 */
	public boolean equals(Date date)
	{
		if(date._day==_day&&date._month==_month&&date._year==_year)
		{
			return true;
		}
		return false;
	}
	
	public static String getTimeStamp()
	{
		Calendar cal = Calendar.getInstance();  		
		return cal.getTime()+""; 
	}
	
	@SuppressWarnings("deprecation")
	public static String getHHMMSS()
	{
		Calendar cal = Calendar.getInstance();  
		int hh = cal.getTime().getHours();
		int mm = cal.getTime().getMinutes();
		int ss = cal.getTime().getSeconds();
		return hh+":"+mm+":"+ss;
	}

	@Override
	public int compareTo(Date date) {
		Calendar cal1 = Calendar.getInstance();
		cal1.set(date.get_year(), date.get_month()-1, date.get_day());
		Calendar cal2 = Calendar.getInstance();
		cal2.set(this.get_year(), this.get_month()-1, this.get_day());

		return cal2.compareTo(cal1);
	}

}
